package it.tukano.odt;

import it.tukano.lang.StringExt;
import it.tukano.text.StringScanner;
import it.tukano.xml.XmlDoc.XmlNode;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;

/**
 * Campo di un "form" open document.
 * @author pgi
 */
public class OdtField {
    private final String name;
    private final String token;
    private final Type type;
    private final Integer size;
    private final Integer index;
    private final OdtFieldEditor editor;
    private final String label;
    private final StringExt defaultValue;
    private final Case cas;
    private final Boolean optional;
    private final StringExt spelltargets;
    private final StringExt mandatory;
    private final StringExt[] exclude;
    private OdtFieldContext context;
    
    /**
     * Inizializza il campo
     * @param name il nome del campo
     * @param properties le proprietà del campo. Le proprietà possono essere:
     * type, richiesto, il tipo del campo
     * size, opzionale, la dimensione del campo
     * index, opzionale, la posizione del campo nella sequenza di campi che
     */
    public OdtField(String name, Properties properties) {
        this.name = name;
        this.type = Type.valueOf(properties.getProperty("type"));
        this.token = "{" + name + "}";
        this.size = new StringExt(properties.getProperty("size", "10")).toInteger();
        this.index = new StringExt(properties.getProperty("index", "0")).toInteger();
        this.label = properties.getProperty("label", name);
        this.defaultValue = new StringExt(properties.getProperty("default"));
        this.optional = new StringExt(properties.getProperty("optional", "false")).toBoolean(false);
        this.cas = Case.valueOf(properties.getProperty("case", "none"));
        this.spelltargets = new StringExt(properties.getProperty("spelltargets", ""));
        this.mandatory = new StringExt(properties.getProperty("mandatory", "false"));
        this.exclude=new StringExt(properties.getProperty("exclude", "")).split("#");
        boolean useScript = new StringExt(properties.getProperty("use_script", "false")).toBoolean(false);
        switch(type) {
            case currency:
                this.editor = new OdtCurrencyEditor(size, label, defaultValue, useScript);
                break;
            case decimal:
                this.editor = new OdtDecimalEditor(size, label, defaultValue);
                break;
            case integer:
                this.editor = new OdtIntegerEditor(size, label, defaultValue, useScript);
                break;
            case string:
                this.editor = new OdtStringEditor(size, label, defaultValue, useScript);
                break;
            case varchar:
                this.editor = new OdtVarcharEditor(size, label, defaultValue, useScript);
                break;
            case check:
                this.editor = new OdtCheckEditor(label, defaultValue);
                break;
            case numeric:
                this.editor = new OdtNumericEditor(size, label, defaultValue);
                break;
            case date:
                this.editor = new OdtDateEditor(size, label, defaultValue);
                break;
            case choice:
                this.editor = new OdtChoiceEditor(size, label, defaultValue);
                break;
            case oplistitem:
                this.editor = new OdtOpListItemEditor(size, label, defaultValue);
                break;
            case optionalstring:
                this.editor = new OdtOptionalStringEditor(size, properties.getProperty("label", ""), defaultValue);
                break;
            case opparagraph:
                this.editor = new OdtOptionalParagraphEditor(size, properties.getProperty("label", ""), defaultValue);
               break;
            case opsection:
                this.editor = new OdtOptionalSectionEditor(defaultValue);
                break;
            default:
                throw new IllegalArgumentException("Unsupported type: " + type);
        }
        this.editor.setProperties(properties);
        if(optional) {
            editor.setOptional();
        }
        if(properties.getProperty("hidden", "false").equals("true")) {
            editor.setVisible(false);
        }
    }
    
    public Collection<StringExt> getExclusionList() {
        return exclude == null ? Collections.<StringExt>emptyList() : Arrays.asList(exclude);
    }

    /**
     * Instance initializer
     */
    public OdtField(XmlNode node) {
        this(node.getString("name"), node.getAttributesAsProperties());
    }
    
    public boolean hasContext() {
        return context != null;
    }
    
    public void setContext(OdtFieldContext context) {
        this.context = context;
    }
    
    public OdtFieldContext getContext() {
        return this.context;
    }

    /** return the name of this field which is the token minus { }*/
    public String getName() {
        return name;
    }

    public Case getCase() {
        return cas;
    }

    /** return the id of this field enclosed in { } */
    public String getToken() {
        return token;
    }

    public Integer getIndex() {
        return index;
    }
    
    public boolean isMandatory() {
        return mandatory.toBoolean(Boolean.FALSE);
    }

    public OdtFieldEditor getEditor() {
        this.editor.setOwner(this);
        return this.editor;
    }

    public Integer getSize() {
        return size;
    }

    public void clearEditedValue() {
        editor.clearValue();
    }
    
    public String[] getSpellTargets() {
        if(spelltargets == null) return new String[0];
        return spelltargets.toString("").split(";");
    }

    public String generateParagraph(String xml, String paragraphContent) {
        String parStart = "<text:p";
        String parEnd = "</text:p>";
        int tokenIndex = xml.indexOf(getToken());
        int paragraphEnd = xml.indexOf(parEnd, tokenIndex) + parEnd.length();
        int paragraphStart = StringScanner.backIndexOf(xml, parStart, tokenIndex);
        String paragraphText = xml.substring(paragraphStart, paragraphEnd);
        OdtParagraph p = new OdtParagraph(paragraphText);
        p.remove(getToken());
        p.appendClonedParagraph(paragraphContent);
        return p.apply(xml);
    }
    
    public int getParagraphEndIndex(String xml) {
        String parEnd = "</text:p>";
        return getElementEndIndex(parEnd, xml);
    }
    
    public int getParagraphStartIndex(String xml) {
        String parStart = "<text:p";
        return getElementStartIndex(parStart, xml);
    }
    
    public int getElementStartIndex(String parStart, String xml) {
        int tokenIndex = xml.indexOf(getToken());
        int paragraphStart = StringScanner.backIndexOf(xml, parStart, tokenIndex);
        return paragraphStart;
    }
    
    public int getElementEndIndex(String parEnd, String xml) {
        int tokenIndex = xml.indexOf(getToken());
        int paragraphEnd = xml.indexOf(parEnd, tokenIndex) + parEnd.length();
        return paragraphEnd;
    }
    
    public int getListItemStartIndex(String xml) {
        String parStart = "<text:list-item";
        return getElementStartIndex(parStart, xml);
    }
    
    public int getListItemEndIndex(String xml) {
        String parEnd = "</text:list-item>";
        return getElementEndIndex(parEnd, xml);
    }
    
    public String eraseWrappingList(String xml) {
        for (int i = getListItemStartIndex(xml); i >= 0; i = getListItemStartIndex(xml)) {
            int e = getListItemEndIndex(xml);
            xml = xml.substring(0, i) + xml.substring(e, xml.length());
        }
        return xml;
    }

    public String eraseWrappingParagraph(String xml) {
        for (int i = getParagraphStartIndex(xml); i >= 0; i = getParagraphStartIndex(xml)) {
            int e = getParagraphEndIndex(xml);
            xml = xml.substring(0, i) + xml.substring(e, xml.length());
        }
        return xml;
    }

    public boolean hasEditor() {
        return editor != null;
    }

    public static enum Type {
        currency,
        decimal,
        integer,
        string,
        varchar,
        check,
        numeric,
        date,
        choice,
        oplistitem,
        optionalstring,
        opparagraph,//the paragraph containing the token will be removed if field is unchecked
        opsection,//remove the paragraphs from the first occurence of the token to the last one
    }

    public static enum Case {
        none,
        upper,
        lower,
        name;

        public String apply(String string) {
            if(this == none) return string;
            if(this == upper) return string.toUpperCase();
            if(this == lower) return string.toLowerCase();
            if(this == name && string.length() > 1) return string.substring(0, 1).toUpperCase() + string.substring(1);
            return string;
        }
    }
}
